`timescale 1ns/1ps

`define CLK_PERIOD          10
`define TCLK_PERIOD         40
`define MAX_RUN_TIME        32'h3000000

`define SOC_TOP             tb.x_soc
`define RTL_MEM             tb.x_soc.x_axi_slave128.x_f_spsram_large

`define CPU_TOP             tb.x_soc.x_cpu_sub_system_axi.x_rv_integration_platform.x_cpu_top
`define tb_retire0          `CPU_TOP.core0_pad_retire0
`define retire0_pc          `CPU_TOP.core0_pad_retire0_pc[39:0]
`define tb_retire1          `CPU_TOP.core0_pad_retire1
`define retire1_pc          `CPU_TOP.core0_pad_retire1_pc[39:0]
`define tb_retire2          `CPU_TOP.core0_pad_retire2
`define retire2_pc          `CPU_TOP.core0_pad_retire2_pc[39:0]
`define CPU_CLK             `CPU_TOP.pll_cpu_clk
`define CPU_RST             `CPU_TOP.pad_cpu_rst_b
`define clk_en              `CPU_TOP.axim_clk_en
`define CP0_RSLT_VLD        `CPU_TOP.x_ct_top_0.x_ct_core.x_ct_cp0_top.x_ct_cp0_iui.cp0_iu_ex3_rslt_vld
`define CP0_RSLT            `CPU_TOP.x_ct_top_0.x_ct_core.x_ct_cp0_top.x_ct_cp0_iui.cp0_iu_ex3_rslt_data[63:0]

// `define APB_BASE_ADDR       40'h4000000000
`define APB_BASE_ADDR       40'hb0000000

module tb();
  reg clk;
  reg jclk;
  reg rst_b;
  reg jrst_b;
  reg jtap_en;
  wire jtg_tms;
  wire jtg_tdi;
  wire jtg_tdo;
  wire  pad_yy_gate_clk_en_b;
  
  static integer FILE;
  
  wire uart0_sin;
  wire [7:0]b_pad_gpio_porta;
  
  assign pad_yy_gate_clk_en_b = 1'b1;
  
  initial
  begin
    clk =0;
    forever begin
      #(`CLK_PERIOD/2) clk = ~clk;
    end
  end
  
  initial 
  begin 
    jclk = 0;
    forever begin
      #(`TCLK_PERIOD/2) jclk = ~jclk;
    end
  end
  
  initial
  begin
    rst_b = 1;
    #100;
    rst_b = 0;
    #100;
    rst_b = 1;
  end
  
  initial
  begin
    jrst_b = 1;
    #400;
    jrst_b = 0;
    #400;
    jrst_b = 1;
  end
 
  integer i;
  bit [31:0] mem_inst_temp [65536];
  bit [31:0] mem_data_temp [65536];
  integer j;
  initial
  begin
    $display("\t********* Init Program *********");
    $display("\t********* Wipe memory to 0 *********");
    for(i=0; i < 32'h16384; i=i+1)
    begin
      `RTL_MEM.ram0.mem[i][7:0] = 8'h0;
      `RTL_MEM.ram1.mem[i][7:0] = 8'h0;
      `RTL_MEM.ram2.mem[i][7:0] = 8'h0;
      `RTL_MEM.ram3.mem[i][7:0] = 8'h0;
      `RTL_MEM.ram4.mem[i][7:0] = 8'h0;
      `RTL_MEM.ram5.mem[i][7:0] = 8'h0;
      `RTL_MEM.ram6.mem[i][7:0] = 8'h0;
      `RTL_MEM.ram7.mem[i][7:0] = 8'h0;
      `RTL_MEM.ram8.mem[i][7:0] = 8'h0;
      `RTL_MEM.ram9.mem[i][7:0] = 8'h0;
      `RTL_MEM.ram10.mem[i][7:0] = 8'h0;
      `RTL_MEM.ram11.mem[i][7:0] = 8'h0;
      `RTL_MEM.ram12.mem[i][7:0] = 8'h0;
      `RTL_MEM.ram13.mem[i][7:0] = 8'h0;
      `RTL_MEM.ram14.mem[i][7:0] = 8'h0;
      `RTL_MEM.ram15.mem[i][7:0] = 8'h0;
    end
  
    $display("\t********* Read program *********");
    $readmemh("inst.pat", mem_inst_temp);
    $readmemh("data.pat", mem_data_temp);
  
    $display("\t********* Load program to memory *********");
    i=0;
    for(j=0;i<32'h4000;i=j/4)
    begin
      `RTL_MEM.ram0.mem[i][7:0] = mem_inst_temp[j][31:24];
      `RTL_MEM.ram1.mem[i][7:0] = mem_inst_temp[j][23:16];
      `RTL_MEM.ram2.mem[i][7:0] = mem_inst_temp[j][15: 8];
      `RTL_MEM.ram3.mem[i][7:0] = mem_inst_temp[j][ 7: 0];
      j = j+1;
      `RTL_MEM.ram4.mem[i][7:0] = mem_inst_temp[j][31:24];
      `RTL_MEM.ram5.mem[i][7:0] = mem_inst_temp[j][23:16];
      `RTL_MEM.ram6.mem[i][7:0] = mem_inst_temp[j][15: 8];
      `RTL_MEM.ram7.mem[i][7:0] = mem_inst_temp[j][ 7: 0];
      j = j+1;
      `RTL_MEM.ram8.mem[i][7:0] = mem_inst_temp[j][31:24];
      `RTL_MEM.ram9.mem[i][7:0] = mem_inst_temp[j][23:16];
      `RTL_MEM.ram10.mem[i][7:0] = mem_inst_temp[j][15: 8];
      `RTL_MEM.ram11.mem[i][7:0] = mem_inst_temp[j][ 7: 0];
      j = j+1;
      `RTL_MEM.ram12.mem[i][7:0] = mem_inst_temp[j][31:24];
      `RTL_MEM.ram13.mem[i][7:0] = mem_inst_temp[j][23:16];
      `RTL_MEM.ram14.mem[i][7:0] = mem_inst_temp[j][15: 8];
      `RTL_MEM.ram15.mem[i][7:0] = mem_inst_temp[j][ 7: 0];
      j = j+1;
    end
    i=0;
    for(j=0;i<32'h4000;i=j/4)
    begin
      `RTL_MEM.ram0.mem[i+32'h4000][7:0]  = mem_data_temp[j][31:24];
      `RTL_MEM.ram1.mem[i+32'h4000][7:0]  = mem_data_temp[j][23:16];
      `RTL_MEM.ram2.mem[i+32'h4000][7:0]  = mem_data_temp[j][15: 8];
      `RTL_MEM.ram3.mem[i+32'h4000][7:0]  = mem_data_temp[j][ 7: 0];
      j = j+1;
      `RTL_MEM.ram4.mem[i+32'h4000][7:0]  = mem_data_temp[j][31:24];
      `RTL_MEM.ram5.mem[i+32'h4000][7:0]  = mem_data_temp[j][23:16];
      `RTL_MEM.ram6.mem[i+32'h4000][7:0]  = mem_data_temp[j][15: 8];
      `RTL_MEM.ram7.mem[i+32'h4000][7:0]  = mem_data_temp[j][ 7: 0];
      j = j+1;
      `RTL_MEM.ram8.mem[i+32'h4000][7:0]   = mem_data_temp[j][31:24];
      `RTL_MEM.ram9.mem[i+32'h4000][7:0]   = mem_data_temp[j][23:16];
      `RTL_MEM.ram10.mem[i+32'h4000][7:0]  = mem_data_temp[j][15: 8];
      `RTL_MEM.ram11.mem[i+32'h4000][7:0]  = mem_data_temp[j][ 7: 0];
      j = j+1;
      `RTL_MEM.ram12.mem[i+32'h4000][7:0]  = mem_data_temp[j][31:24];
      `RTL_MEM.ram13.mem[i+32'h4000][7:0]  = mem_data_temp[j][23:16];
      `RTL_MEM.ram14.mem[i+32'h4000][7:0]  = mem_data_temp[j][15: 8];
      `RTL_MEM.ram15.mem[i+32'h4000][7:0]  = mem_data_temp[j][ 7: 0];
      j = j+1;
    end
  end

  initial
  begin
  #(`MAX_RUN_TIME * `CLK_PERIOD);
    $display("**********************************************");
    $display("*   meeting max simulation time, stop!       *");
    $display("**********************************************");
    FILE = $fopen("run_case.report","w");
    $fwrite(FILE,"TEST FAIL");   
  $finish;
  end
  
  reg [31:0] retire_inst_in_period;
  reg [31:0] cycle_count;
  
  `define LAST_CYCLE 50000
  always @(posedge clk or negedge rst_b)
  begin
    if(!rst_b)
      cycle_count[31:0] <= 32'b1;
    else 
      cycle_count[31:0] <= cycle_count[31:0] + 1'b1;
  end
  
  
    always @(posedge clk or negedge rst_b)
    begin
      if(!rst_b) //reset to zero
        retire_inst_in_period[31:0] <= 32'b0;
      else if( (cycle_count[31:0] % `LAST_CYCLE) == 0)//check and reset retire_inst_in_period every 50000 cycles
      begin
        if(retire_inst_in_period[31:0] == 0)begin
          $display("*************************************************************");
          $display("* Error: There is no instructions retired in the last %d cycles! *", `LAST_CYCLE);
          $display("*              Simulation Fail and Finished!                *");
          $display("*************************************************************");
          #10;
          FILE = $fopen("run_case.report","w");
          $fwrite(FILE,"TEST FAIL");   
    
          $finish;
        end
        retire_inst_in_period[31:0] <= 32'b0;
      end
      else if(`tb_retire0 || `tb_retire1 || `tb_retire2)
        retire_inst_in_period[31:0] <= retire_inst_in_period[31:0] + 1'b1;
    end
  
  
  
    reg [31:0] cpu_awaddr;
    reg [3:0]  cpu_awlen;
    reg [15:0] cpu_wstrb;
    reg        cpu_wvalid;
    reg [63:0] value0;
    reg [63:0] value1;
    reg [63:0] value2;
  
    bit  [31:0]  sw_data;
    bit          sw_fail;
    bit          sw_error;
    bit          sw_finish;

  
    always @(posedge clk) begin
        cpu_awlen[3:0]   <= `SOC_TOP.x_axi_slave128.awlen[3:0];
        cpu_awaddr[31:0] <= `SOC_TOP.x_axi_slave128.mem_addr[31:0];
        cpu_wvalid       <= `SOC_TOP.biu_pad_wvalid;
        cpu_wstrb        <= `SOC_TOP.biu_pad_wstrb;
        // value0           <= `CPU_TOP.core0_pad_wb0_data[63:0];
        // value1           <= `CPU_TOP.core0_pad_wb1_data[63:0];
        // value2           <= `CPU_TOP.core0_pad_wb2_data[63:0];
        value0              <= `CPU_TOP.x_ct_top_0.x_ct_core.x_ct_iu_top.x_ct_iu_rbus.rbus_pipe0_wb_data[63:0];
        value1              <= `CPU_TOP.x_ct_top_0.x_ct_core.x_ct_iu_top.x_ct_iu_rbus.rbus_pipe1_wb_data[63:0];
        value2              <= `CPU_TOP.x_ct_top_0.x_ct_core.x_ct_lsu_top.x_ct_lsu_ld_wb.ld_wb_preg_data_sign_extend[63:0];
    end
  
    always @(posedge clk) begin
        if(value0 == 64'h444333222 || value1 == 64'h444333222 || value2 == 64'h444333222) begin
            $display("**********************************************");
            $display("*    simulation finished successfully        *");
            $display("**********************************************");
            #10;
            FILE = $fopen("run_case.report","w");
            $fwrite(FILE,"TEST PASS");   
            sw_finish = 1;
            $finish(2);
        end
        else if (value0 == 64'h2382348720 || value1 == 64'h2382348720 || value2 == 64'h444333222) begin
            $display("**********************************************");
            $display("*    simulation finished with error          *");
            $display("**********************************************");
            #10;
            FILE = $fopen("run_case.report","w");
            $fwrite(FILE,"TEST FAIL");   
            sw_fail = 1;
            sw_finish = 1;
            $finish(2);
        end
    end

    initial begin

        int fd;
        string str_q, filename;
        filename = "bus_monitor.log"; 
        $timeformat(-6, 6, "us", 10);
        forever begin
            @(posedge clk iff((cpu_awlen[3:0] == 4'b0) && (cpu_awaddr[31:4] == 28'h01ff_fff) && cpu_wvalid && `CPU_TOP.axim_clk_en))
            fd = $fopen(filename, "a"); 
            if(cpu_wstrb[15:0] == 16'hf) begin
                if((`SOC_TOP.biu_pad_wdata[7:0] == 8'h00) | (`SOC_TOP.biu_pad_wdata[7:0] == 8'h0a)) begin
                    $fwrite(fd, "\n");
                    $display("[debug_log]@%0t: %s", $time, str_q);
                    str_q = {""};
                end else begin 
                    $fwrite(fd, "%c", `SOC_TOP.biu_pad_wdata[7:0]);
                    str_q = {str_q, string'(`SOC_TOP.biu_pad_wdata[7:0])};
                end 
            end
            else if(cpu_wstrb[15:0] == 16'hf0) begin
                fd = $fopen(filename, "a");
                $fwrite(fd, "0x%8h\n", `SOC_TOP.biu_pad_wdata[63:32]);
                $display("[debug_info]@%0t: 0x%8h", $time, `SOC_TOP.biu_pad_wdata[63:32]);
                sw_data <= `SOC_TOP.biu_pad_wdata[63:32];
                $fclose(fd); 
            end
            else if(cpu_wstrb[15:0] == 16'hf00) begin
                $fwrite(fd, "0x%8h\n", `SOC_TOP.biu_pad_wdata[95:64]);
                $display("[debug_error]@%0t: 0x%8h", $time, `SOC_TOP.biu_pad_wdata[95:64]);
                sw_error <= `SOC_TOP.biu_pad_wdata[95:64];
                sw_fail <= 1;
                $fclose(fd); 
            end
            else if(cpu_wstrb[15:0] == 16'hf000) begin
                if(`SOC_TOP.biu_pad_wdata[127:96] == 32'h7E57E00D) begin
                    $display("[debug_pass]@%0t: Software Flow finish.", $time);
                    sw_finish = 1; 
                end
                else if(`SOC_TOP.biu_pad_wdata[127:96] == 32'h7E57FA10) begin
                    $error("[debug_fail]@%0t: Software Flow error!!!", $time);
                    sw_fail = 1;
                end
            end
            $fclose(fd); 
        end
    end  
 
    final begin
        $display("\n");
        $timeformat(-3, 12, "ms", 20);
        if ((sw_fail == 1'b1) || (sw_error == 1'b1))  begin
            $display("=========================================================");
            $display("TestCase Failed !!!!!!");
            $display("=========================================================");
        end
        else begin
            $display("=========================================================");
            $display("TestCase Passed.");
            $display("=========================================================");
        end
        $display("\n");
    end 

  parameter cpu_cycle = 110;
  
`ifndef NO_DUMP
    initial begin
`ifdef NC_SIM
        $dumpfile("test.vcd");
        $dumpvars;  
`else
    `ifdef IVERILOG_SIM
        $dumpfile("test.vcd");
        $dumpvars;  
    `else
        $fsdbDumpvars();
    `endif
`endif
    end
`endif
  
  assign jtg_tdi = 1'b0;
  assign uart0_sin = 1'b1;
  
  
  soc x_soc(
    .i_pad_clk           ( clk                  ),
    .b_pad_gpio_porta    ( b_pad_gpio_porta     ),
    .i_pad_jtg_trst_b    ( jrst_b               ),
    .i_pad_jtg_tclk      ( jclk                 ),
    .i_pad_jtg_tdi       ( jtg_tdi              ),
    .i_pad_jtg_tms       ( jtg_tms              ),
    .i_pad_uart0_sin     ( uart0_sin            ),
    .o_pad_jtg_tdo       ( jtg_tdo              ),
    .o_pad_uart0_sout    ( uart0_sout           ),
    .i_pad_rst_b         ( rst_b                )
  );
  

// Latest Power control
`ifdef UPF_INCLUDED
  import UPF::*;

  initial
  begin
        supply_on ("VDD", 1.00);
        supply_on ("VDDG", 1.00);
  end

  initial 
  begin
    $deposit(tb.x_soc.pmu_cpu_pwr_on,  1'b1);
    $deposit(tb.x_soc.pmu_cpu_iso_in,  1'b0);
    $deposit(tb.x_soc.pmu_cpu_iso_out, 1'b0);
    $deposit(tb.x_soc.pmu_cpu_save,    1'b0);
    $deposit(tb.x_soc.pmu_cpu_restore, 1'b0);
  end
`endif
  
  reg [31:0] virtual_counter;
  
  always @(posedge `CPU_CLK or negedge `CPU_RST)
  begin
    if(!`CPU_RST)
      virtual_counter[31:0] <= 32'b0;
    else if(virtual_counter[31:0]==32'hffffffff)
      virtual_counter[31:0] <= virtual_counter[31:0];
    else
      virtual_counter[31:0] <= virtual_counter[31:0] +1'b1;
  end 
  
  //always @(*)
  //begin
  //if(virtual_counter[31:0]> 32'h3000000) $finish;
  //end
  
endmodule

